<template>
    <div class="time-echarts">
        <!-- 使用传送门组件将tip放到body下 -->
        <component :is="portalComponent" to="body" v-if="hasPortalSupport">
            <!-- 选中时间块的tip -->
            <div
                v-show="tipVisible"
                class="time-tip"
                :style="{
                    left: `${tipPosition.x}px`,
                    top: `${tipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ tipContent }}
            </div>

            <!-- 预览时间块的tip -->
            <div
                v-show="previewTipVisible"
                class="time-tip preview-tip"
                :style="{
                    left: `${previewTipPosition.x}px`,
                    top: `${previewTipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ previewTipContent }}
            </div>
        </component>

        <!-- 不支持传送门的回退方案 -->
        <div class="tip-container" :style="{ zIndex: tipZIndex }" v-else>
            <!-- 选中时间块的tip -->
            <div
                v-show="tipVisible"
                class="time-tip"
                :style="{
                    left: `${tipPosition.x}px`,
                    top: `${tipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ tipContent }}
            </div>

            <!-- 预览时间块的tip -->
            <div
                v-show="previewTipVisible"
                class="time-tip preview-tip"
                :style="{
                    left: `${previewTipPosition.x}px`,
                    top: `${previewTipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ previewTipContent }}
            </div>
        </div>

        <div class="contents-day">
            <div class="bottom">
                <div class="leftbar">
                    <div class="weekday-day" :style="dayStyle">
                        <span>一天</span>
                    </div>
                </div>
                <div class="right-day" ref="rightDayRef" :style="rightDayStyle">
                    <div class="grid-container">
                        <div v-for="i in 24" :key="i" class="grid-column"></div>
                    </div>
                    <canvas
                        ref="canvasRef"
                        :style="{
                            cursor: dragState.isDragging ? 'col-resize' : 'pointer',
                            pointerEvents: 'auto',
                        }"
                    ></canvas>
                </div>
            </div>
            <div class="bottomhour-day">
                <ul class="ul-day">
                    <li v-for="hour in hours" :key="hour" :style="{ width: `${smallestUnit}px` }">
                        {{ hour }}
                    </li>
                </ul>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue-demi';
import { isVue2 } from 'vue-demi';

interface DragState {
    isDragging: boolean;
    startX: number;
    startY: number;
    hasMoved: boolean;
    isMovingBlock: boolean;
    movingStartX: number;
    movingStartY: number;
    selectedBlockStartX: number;
    selectedBlockWidth: number;
    canMoveBlock: boolean;
    isResizing: boolean;
    resizeDirection: 'left' | 'right' | null;
    clickedPlan: { startDate: string; endDate: string } | null;
}

const props = defineProps<{
    timePlan: Array<{ startDate: string; endDate?: string }>;
}>();

const emit = defineEmits(['timeSelect', 'layoutChange']);

const MINUTES_PER_HOUR = 60;
const MINUTES_PER_GRID = 15;
const MAX_BLOCK_HEIGHT = 40;
const GRIDS_PER_HOUR = 4; // 每小时4个格子

const rightDayRef = ref<HTMLDivElement | null>(null);
const canvasRef = ref<HTMLCanvasElement | null>(null);
const hours = Array.from({ length: 24 }, (_, i) => i);
const smallestUnit = ref(0);

const blockHeight = computed(() =>
    smallestUnit.value > MAX_BLOCK_HEIGHT ? MAX_BLOCK_HEIGHT : smallestUnit.value,
);

const dayStyle = computed(() => ({
    height: `${blockHeight.value}px`,
    lineHeight: `${blockHeight.value}px`,
}));

const rightDayStyle = computed(() => ({
    height: `${blockHeight.value}px`,
}));

// 维护本地的时间计划数组
const localTimePlan = ref<Array<{ startDate: string; endDate: string }>>([]);

const dragState = ref<DragState>({
    isDragging: false,
    startX: 0,
    startY: 0,
    hasMoved: false,
    isMovingBlock: false,
    movingStartX: 0,
    movingStartY: 0,
    selectedBlockStartX: 0,
    selectedBlockWidth: 0,
    canMoveBlock: false,
    isResizing: false,
    resizeDirection: null,
    clickedPlan: null,
});

// 添加一个变量来存储 tip 相对于时间块的偏移量
const tipOffset = ref({ x: 0, y: 0 });

// 添加 tip 相关的响应式数据
const tipVisible = ref(false);
const tipContent = ref('');
const tipPosition = ref({ x: 0, y: 0 });
// 添加动态 z-index 值
const tipZIndex = ref(1000);

// 添加预览tip相关的状态
const previewTipVisible = ref(false);
const previewTipContent = ref('');
const previewTipPosition = ref({ x: 0, y: 0 });

// 获取页面最大 z-index 值的函数
const getMaxZIndex = (): number => {
    let maxZIndex = 0;
    const allElements = document.getElementsByTagName('*');
    for (let i = 0; i < allElements.length; i++) {
        const element = allElements[i];
        const style = window.getComputedStyle(element);
        const zIndex = parseInt(style.zIndex, 10);
        if (!isNaN(zIndex) && zIndex > maxZIndex) {
            maxZIndex = zIndex;
        }
    }
    return maxZIndex;
};

// 更新 tip 的 z-index 值
const updateTipZIndex = () => {
    tipZIndex.value = getMaxZIndex() + 1;
};

// 修改 findClickedTimePlan 函数，移除调试代码
const findClickedTimePlan = (x: number): { startDate: string; endDate: string } | null => {
    const gridWidth = canvasRef.value?.width ? canvasRef.value.width / 24 : 0;

    // 首先检查当前选中的时间块
    if (selectedTimePlan.value) {
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (startTime && endTime) {
            // 计算点击位置对应的小时
            const clickHour = x / gridWidth;

            // 计算时间块的范围（小时）
            const startHour = startTime.hours + startTime.minutes / 60;
            const endHour = endTime.hours + endTime.minutes / 60;

            // 检查点击位置是否在时间块范围内，适当扩大点击区域以便更容易选中
            const isInRange =
                clickHour >= (startHour * gridWidth - 10) / gridWidth &&
                clickHour <= (endHour * gridWidth + 10) / gridWidth;

            if (isInRange) {
                return selectedTimePlan.value; // 如果点击在选中时间块上，直接返回它
            }
        }
    }

    // 如果没有点击在选中的时间块上，则检查其他时间块
    // 反转数组以便从最后添加的时间块开始检查
    // 这样可以优先选中最上层（最新）的时间块
    const result =
        [...localTimePlan.value].reverse().find((plan) => {
            // 如果是已选中的时间块，跳过（因为已经检查过了）
            if (
                selectedTimePlan.value &&
                plan.startDate === selectedTimePlan.value.startDate &&
                plan.endDate === selectedTimePlan.value.endDate
            ) {
                return false;
            }

            const startTime = parseTime(plan.startDate);
            const endTime = parseTime(plan.endDate);
            if (!startTime || !endTime) return false;

            // 计算点击位置对应的小时
            const clickHour = x / gridWidth;

            // 计算时间块的范围（小时）
            const startHour = startTime.hours + startTime.minutes / 60;
            const endHour = endTime.hours + endTime.minutes / 60;

            // 检查点击位置是否在时间块范围内
            const isInRange =
                clickHour >= (startHour * gridWidth - 10) / gridWidth &&
                clickHour <= (endHour * gridWidth + 10) / gridWidth;
            return isInRange;
        }) || null;

    return result;
};

// 添加状态来跟踪当前选中的时间块
const selectedTimePlan = ref<{ startDate: string; endDate: string } | null>(null);

// 修改 checkResizeZone 函数，确保包含边界点
const checkResizeZone = (
    x: number,
    plan: { startDate: string; endDate: string },
): 'left' | 'right' | null => {
    const gridWidth = canvasRef.value?.width ? canvasRef.value.width / 24 : 0;
    const startTime = parseTime(plan.startDate);
    const endTime = parseTime(plan.endDate);
    if (!startTime || !endTime) return null;

    // 计算时间块的开始和结束位置
    const startX = (startTime.hours + startTime.minutes / 60) * gridWidth;
    const endX = (endTime.hours + endTime.minutes / 60) * gridWidth;

    // 计算时间块的总宽度
    const blockWidth = endX - startX;

    // 根据时间块宽度动态计算合适的边缘检测区域
    // 对于短时间块（如15分钟），使用更小的边缘区域
    // 对于较长时间块，保持原有的固定区域大小
    // 确保边缘区域最小为2像素，最大为10像素，且不超过时间块宽度的25%
    let resizeZone = Math.min(10, Math.max(2, blockWidth * 0.25));

    // 15分钟时间块的特殊处理
    // 检查是否是15分钟时间块（通常宽度较小）
    const minutes15Width = gridWidth / 4; // 15分钟在网格中的宽度
    if (Math.abs(blockWidth - minutes15Width) < 1) {
        // 允许1像素的误差
        // 对于15分钟的时间块，使用更小的边缘区域
        resizeZone = Math.min(2, blockWidth * 0.2);
    }

    // 修改左侧检测逻辑：包含起始位置和向外延伸的区域
    if (Math.abs(x - startX) <= resizeZone) return 'left';
    // 修改右侧检测逻辑：包含结束位置和向外延伸的区域
    if (Math.abs(x - endX) <= resizeZone) return 'right';

    return null;
};

// 修改 updateTip 函数，添加防抖机制
let updateTipTimeout: number | null = null;

const updateTip = () => {
    if (!selectedTimePlan.value || !canvasRef.value) {
        tipVisible.value = false;
        return;
    }

    // 获取画布和容器的尺寸信息
    const canvas = canvasRef.value;
    const timeEcharts = canvas.closest('.time-echarts');
    if (!timeEcharts) {
        tipVisible.value = false;
        return;
    }

    const canvasRect = canvas.getBoundingClientRect();
    const selectedStart = selectedTimePlan.value.startDate;
    const selectedEnd = selectedTimePlan.value.endDate;

    // 计算时间块的坐标
    const timePosition = getTimeBlockPosition(selectedStart, selectedEnd);

    // 更新tip内容
    tipContent.value = `${selectedStart} - ${selectedEnd}`;

    // 计算tip位置
    tipPosition.value = {
        x: canvasRect.left + (timePosition.startX + timePosition.endX) / 2,
        y: canvasRect.top - 10,
    };

    // 更新z-index
    updateTipZIndex();

    // 显示tip
    tipVisible.value = true;
};

// 添加getTimeBlockPosition函数
// 计算时间块在画布上的位置
const getTimeBlockPosition = (startDate: string, endDate: string) => {
    const canvas = canvasRef.value;
    if (!canvas) {
        return { startX: 0, endX: 0 };
    }

    const gridWidth = canvas.width / 24;

    // 解析时间字符串
    const startParts = startDate.split(':').map(Number);
    const endParts = endDate.split(':').map(Number);

    // 计算小时和分钟
    const startHours = startParts[0];
    const startMinutes = startParts[1];
    const endHours = endParts[0];
    const endMinutes = endParts[1];

    // 转换为位置
    const startX = (startHours + startMinutes / 60) * gridWidth;
    const endX = (endHours + endMinutes / 60) * gridWidth;

    return { startX, endX };
};

// 修改 handleMouseDown 函数，在开始移动时记录 tip 的初始偏移量
const handleMouseDown = (e: MouseEvent) => {
    // 添加 user-select: none
    document.body.style.userSelect = 'none';

    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    const clickX = e.clientX - rect.left;
    const clickedPlan = findClickedTimePlan(clickX);

    if (clickedPlan) {
        // 如果点击了时间块，记录 tip 相对于时间块的偏移量
        const startTime = parseTime(clickedPlan.startDate);
        const endTime = parseTime(clickedPlan.endDate);
        if (startTime && endTime) {
            const gridWidth = canvas.width / 24;
            const startX = (startTime.hours + startTime.minutes / 60) * gridWidth;
            const endX = (endTime.hours + endTime.minutes / 60) * gridWidth;
            const centerX = startX + (endX - startX) / 2;

            // 记录 tip 相对于时间块中心的偏移量
            tipOffset.value = {
                x: tipPosition.value.x - (rect.left + centerX),
                y: tipPosition.value.y - rect.top,
            };
        }
    }

    // 如果点击的是已选中的时间块，处理移动和调整大小
    if (
        clickedPlan &&
        selectedTimePlan.value &&
        clickedPlan.startDate === selectedTimePlan.value.startDate &&
        clickedPlan.endDate === selectedTimePlan.value.endDate
    ) {
        // 检查是否点击在边缘调整区域
        const resizeDirection = checkResizeZone(clickX, clickedPlan);
        if (resizeDirection) {
            dragState.value.isResizing = true;
            dragState.value.resizeDirection = resizeDirection;
            // 添加：确保更新观察者监控可见性
            requestAnimationFrame(() => {
                observeSelectedTimeBlock();
            });
            return;
        }

        // 设置移动状态
        dragState.value.isMovingBlock = true;
        dragState.value.movingStartX = clickX;
        dragState.value.movingStartY = e.clientY - rect.top;
        dragState.value.canMoveBlock = true;

        const startTime = parseTime(clickedPlan.startDate);
        const endTime = parseTime(clickedPlan.endDate);
        if (startTime && endTime) {
            const gridWidth = canvas.width / 24;
            dragState.value.selectedBlockStartX =
                (startTime.hours + startTime.minutes / 60) * gridWidth;
            dragState.value.selectedBlockWidth =
                (endTime.hours +
                    endTime.minutes / 60 -
                    (startTime.hours + startTime.minutes / 60)) *
                gridWidth;
        }
        // 添加：确保更新观察者监控可见性
        requestAnimationFrame(() => {
            observeSelectedTimeBlock();
        });
        return;
    }

    // 记录点击的时间块，但不立即选中 - 让用户有机会拖动创建新的时间块
    dragState.value.clickedPlan = clickedPlan;

    // 设置拖拽状态
    dragState.value.isDragging = true;
    dragState.value.startX = clickX;
    dragState.value.startY = e.clientY - rect.top;
    dragState.value.hasMoved = false;

    // 注意：移除了之前添加的立即选中时间块的逻辑，因为这会阻止拖动创建新的时间块
    // 选中时间块将在 mouseUp 时处理，如果用户没有拖动
};

// 修改 handleMouseMove 函数，在移动时直接更新 tip 位置
const handleMouseMove = (e: MouseEvent) => {
    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    const mouseX = e.clientX - rect.left;
    const mouseY = e.clientY - rect.top;

    // 如果有选中的时间块，检查是否在边缘
    if (selectedTimePlan.value && !dragState.value.isMovingBlock && !dragState.value.isResizing) {
        const resizeDirection = checkResizeZone(mouseX, selectedTimePlan.value);
        canvas.style.cursor = resizeDirection ? 'ew-resize' : 'pointer';
    }

    // 处理时间块的移动
    if (dragState.value.isMovingBlock && selectedTimePlan.value) {
        const currentX = e.clientX - rect.left;
        const deltaX = currentX - dragState.value.movingStartX;

        const gridWidth = canvas.width / 24;

        // 计算当前时间块的位置
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (!startTime || !endTime) return;

        // 计算时间块的持续时间（小时）
        let duration =
            endTime.hours + endTime.minutes / 60 - (startTime.hours + startTime.minutes / 60);

        // 确保duration至少为0.25小时（15分钟），解决浮点数精度问题
        if (duration < 0.25) {
            duration = 0.25; // 15分钟
        }

        // 计算新的开始时间（小时）
        let newStartHour = startTime.hours + startTime.minutes / 60 + deltaX / gridWidth;

        // 边界处理：如果新的开始时间小于 0，强制设为 0
        if (newStartHour < 0) {
            newStartHour = 0;
        }

        // 如果新的结束时间大于 23.98 (23:59)，强制开始时间调整到合适位置
        // 23.98是23小时59分钟转换为小时的近似值
        if (newStartHour + duration > 23.98) {
            newStartHour = 23.98 - duration;
        }

        // 将小时转换为时分格式
        let newStartHourFloor = Math.floor(newStartHour);
        let newStartMinutes = Math.round(((newStartHour - newStartHourFloor) * 60) / 15) * 15;

        // 处理分钟数为60的情况
        if (newStartMinutes === 60) {
            newStartHourFloor += 1;
            newStartMinutes = 0;
        }

        // 计算新的结束时间 - 使用更精确的方法
        // 先转换为总分钟数进行计算，避免浮点数精度问题
        const startTotalMinutes = newStartHourFloor * 60 + newStartMinutes;
        const durationMinutes = Math.round(duration * 60);
        const endTotalMinutes = startTotalMinutes + durationMinutes;

        let newEndHourFloor = Math.floor(endTotalMinutes / 60);
        let newEndMinutes = endTotalMinutes % 60;

        // 确保结束时间舍入到最接近的15分钟
        newEndMinutes = Math.round(newEndMinutes / 15) * 15;

        // 处理分钟数为60的情况
        if (newEndMinutes === 60) {
            newEndHourFloor += 1;
            newEndMinutes = 0;
        }

        // 确保结束时间不超过 23:59
        if (newEndHourFloor >= 24 || (newEndHourFloor === 23 && newEndMinutes > 59)) {
            newEndHourFloor = 23;
            newEndMinutes = 59;

            // 如果结束时间被限制，重新调整持续时间并计算开始时间
            if (newStartHour + duration > 23.98) {
                const maxDuration = 23.98 - newStartHour;
                // 如果最大可能持续时间小于15分钟，则调整开始时间
                if (maxDuration < 0.25) {
                    // 0.25小时 = 15分钟
                    // 将结束时间固定在 23:59，将开始时间设为结束时间前15分钟
                    newEndHourFloor = 23;
                    newEndMinutes = 59;
                    newStartHour = 23.75; // 23:45
                    newStartHourFloor = 23;
                    newStartMinutes = 45;
                }
            }
        }

        // 最后验证：确保结束时间始终至少比开始时间晚15分钟
        const finalStartTotalMinutes = newStartHourFloor * 60 + newStartMinutes;
        const finalEndTotalMinutes = newEndHourFloor * 60 + newEndMinutes;

        // 如果时间差小于15分钟，调整结束时间
        if (finalEndTotalMinutes - finalStartTotalMinutes < 15) {
            const correctedEndTotalMinutes = finalStartTotalMinutes + 15;
            newEndHourFloor = Math.floor(correctedEndTotalMinutes / 60);
            newEndMinutes = correctedEndTotalMinutes % 60;

            // 如果超过23:59，则调整开始时间
            if (newEndHourFloor >= 24 || (newEndHourFloor === 23 && newEndMinutes > 59)) {
                newEndHourFloor = 23;
                newEndMinutes = 59;

                // 往前调整开始时间
                const correctedStartTotalMinutes = newEndHourFloor * 60 + newEndMinutes - 15;
                newStartHourFloor = Math.floor(correctedStartTotalMinutes / 60);
                newStartMinutes = correctedStartTotalMinutes % 60;
            }
        }

        // 格式化最终时间
        const finalStartDate = `${String(newStartHourFloor).padStart(2, '0')}:${String(newStartMinutes).padStart(2, '0')}`;
        const finalEndDate =
            newEndHourFloor === 24 && newEndMinutes === 0
                ? '23:59'
                : `${String(newEndHourFloor).padStart(2, '0')}:${String(newEndMinutes).padStart(2, '0')}`;

        // 更新时间块
        const index = localTimePlan.value.findIndex(
            (plan) =>
                plan.startDate === selectedTimePlan.value?.startDate &&
                plan.endDate === selectedTimePlan.value?.endDate,
        );

        if (index !== -1) {
            // 创建新的时间块对象
            const updatedPlan = {
                startDate: finalStartDate,
                endDate: finalEndDate,
            };

            // 更新本地数组和选中状态
            localTimePlan.value[index] = updatedPlan;
            selectedTimePlan.value = updatedPlan;

            // 获取容器和画布的位置信息
            const timeEcharts = canvas.closest('.time-echarts');
            if (timeEcharts) {
                const containerRect = timeEcharts.getBoundingClientRect();
                const canvasRect = canvas.getBoundingClientRect();

                // 计算时间块的中心位置
                const startX = (newStartHourFloor + newStartMinutes / 60) * gridWidth;
                const endX = (newEndHourFloor + newEndMinutes / 60) * gridWidth;
                const centerX = startX + (endX - startX) / 2;

                // 更新 tip 的 z-index 值，确保它拥有最高层级
                updateTipZIndex();

                // 更新 tip 位置，使用相对于容器的位置
                tipPosition.value = {
                    x: canvasRect.left - containerRect.left + centerX,
                    y: canvasRect.top - containerRect.top - 10,
                };
                tipVisible.value = true;
            }

            // 重绘时间块
            drawTimeBlocks();
        }

        // 更新移动起始位置
        dragState.value.movingStartX = currentX;
    }

    // 处理调整大小
    if (dragState.value.isResizing && selectedTimePlan.value) {
        const gridWidth = canvas.width / 24;
        const quarterWidth = gridWidth / 4;

        // 检查鼠标是否超出画布边界
        let boundedMouseX = mouseX;
        if (mouseX < 0) {
            boundedMouseX = 0; // 如果鼠标超出左边界，直接设为0
        } else if (mouseX > canvas.width) {
            boundedMouseX = canvas.width; // 如果鼠标超出右边界，直接设为画布宽度
        }

        const currentQuarter = Math.round(boundedMouseX / quarterWidth);

        // 找到当前选中时间块在数组中的索引
        const index = localTimePlan.value.findIndex(
            (plan) =>
                plan.startDate === selectedTimePlan.value?.startDate &&
                plan.endDate === selectedTimePlan.value?.endDate,
        );

        if (index === -1) return;

        if (dragState.value.resizeDirection === 'left') {
            const endTime = parseTime(selectedTimePlan.value.endDate);
            if (!endTime) return;

            // 当鼠标拖到画布外左侧时，直接设置为00:00
            let newStartHour = 0;
            let newStartMinutes = 0;

            if (mouseX >= 0) {
                newStartHour = Math.floor(currentQuarter / 4);
                newStartMinutes = (currentQuarter % 4) * 15;
            }

            // 计算与结束时间的差值（分钟）
            const endMinutes = endTime.hours * 60 + endTime.minutes;
            const newStartTotalMinutes = newStartHour * 60 + newStartMinutes;
            const timeDiff = endMinutes - newStartTotalMinutes;

            // 如果时间差小于15分钟，调整开始时间
            if (timeDiff < 15) {
                const adjustedStartMinutes = endMinutes - 15;
                const adjustedStartHour = Math.floor(adjustedStartMinutes / 60);
                const adjustedMinutes = adjustedStartMinutes % 60;

                // 更新开始时间
                const newStartDate = `${adjustedStartHour.toString().padStart(2, '0')}:${adjustedMinutes.toString().padStart(2, '0')}`;
                localTimePlan.value[index].startDate = newStartDate;
            } else {
                // 更新开始时间
                const newStartDate = `${newStartHour.toString().padStart(2, '0')}:${newStartMinutes.toString().padStart(2, '0')}`;
                localTimePlan.value[index].startDate = newStartDate;
            }
        } else if (dragState.value.resizeDirection === 'right') {
            const startTime = parseTime(selectedTimePlan.value.startDate);
            if (!startTime) return;

            // 当鼠标拖到画布外右侧时，直接设置为23:59
            let newEndHour = 23;
            let newEndMinutes = 59;

            if (mouseX <= canvas.width) {
                newEndHour = Math.floor(currentQuarter / 4);
                newEndMinutes = (currentQuarter % 4) * 15;
            }

            // 处理边界情况
            if (newEndHour > 23 || (newEndHour === 23 && newEndMinutes > 59)) {
                newEndHour = 23;
                newEndMinutes = 59;
            }

            // 计算与开始时间的差值（分钟）
            const startMinutes = startTime.hours * 60 + startTime.minutes;
            const newEndTotalMinutes = newEndHour * 60 + newEndMinutes;
            const timeDiff = newEndTotalMinutes - startMinutes;

            // 如果时间差小于15分钟，调整结束时间
            if (timeDiff < 15) {
                const adjustedEndMinutes = startMinutes + 15;
                const adjustedEndHour = Math.floor(adjustedEndMinutes / 60);
                const adjustedMinutes = adjustedEndMinutes % 60;

                // 确保调整后的时间不超过23:59
                if (adjustedEndHour >= 24 || (adjustedEndHour === 23 && adjustedMinutes > 59)) {
                    newEndHour = 23;
                    newEndMinutes = 59;
                } else {
                    newEndHour = adjustedEndHour;
                    newEndMinutes = adjustedMinutes;
                }
            }

            // 最终边界检查，确保不会出现24:00
            if (newEndHour === 24 && newEndMinutes === 0) {
                newEndHour = 23;
                newEndMinutes = 59;
            }

            // 更新结束时间
            const newEndDate = `${newEndHour.toString().padStart(2, '0')}:${newEndMinutes.toString().padStart(2, '0')}`;
            localTimePlan.value[index].endDate = newEndDate;
        }

        // 更新选中的时间块
        selectedTimePlan.value = localTimePlan.value[index];

        // 更新 tip 位置
        updateTip();

        // 重绘时间块
        drawTimeBlocks();
        return;
    }

    // 处理拖拽创建新时间段的预览
    if (
        dragState.value.isDragging &&
        !dragState.value.isMovingBlock &&
        !dragState.value.isResizing
    ) {
        const moveDistance = Math.sqrt(
            Math.pow(mouseX - dragState.value.startX, 2) +
                Math.pow(mouseY - dragState.value.startY, 2),
        );

        if (moveDistance > 5) {
            dragState.value.hasMoved = true;

            const canvas = canvasRef.value;
            const timeEcharts = canvas?.closest('.time-echarts');
            if (!canvas || !timeEcharts) return;

            const canvasRect = canvas.getBoundingClientRect();
            const gridWidth = canvas.width / 24;
            const quarterWidth = gridWidth / 4;

            // 计算开始和结束位置（保持使用 Math.min 和 Math.max 确保正确的顺序）
            let startPosition = Math.min(dragState.value.startX, mouseX);
            let endPosition = Math.max(dragState.value.startX, mouseX);

            // 限制开始位置不小于0（00:00）
            startPosition = Math.max(0, startPosition);

            // 限制结束位置不超过画布宽度（对应23:59）
            endPosition = Math.min(endPosition, canvas.width);

            // 计算时间
            const startQuarters = Math.round(startPosition / quarterWidth);
            const startHour = Math.floor(startQuarters / 4);
            const startMinutes = (startQuarters % 4) * 15;

            const endQuarters = Math.round(endPosition / quarterWidth);
            let endHour = Math.floor(endQuarters / 4);
            let endMinutes = (endQuarters % 4) * 15;

            // 处理分钟数为60的情况
            if (endMinutes === 60) {
                endHour += 1;
                endMinutes = 0;
            }

            // 处理边界情况
            if (endHour >= 23 && endMinutes > 59) {
                endHour = 23;
                endMinutes = 59;
            }

            // 计算时间差（分钟）
            const startTotalMinutes = startHour * 60 + startMinutes;
            const endTotalMinutes = endHour * 60 + endMinutes;
            const timeDiff = endTotalMinutes - startTotalMinutes;

            // 确保预览时也至少有15分钟差距
            let adjustedEndHour = endHour;
            let adjustedEndMinutes = endMinutes;
            if (timeDiff < 15 && startPosition !== endPosition) {
                const adjustedEndTotalMinutes = startTotalMinutes + 15;
                adjustedEndHour = Math.floor(adjustedEndTotalMinutes / 60);
                adjustedEndMinutes = adjustedEndTotalMinutes % 60;

                // 确保不超过23:59
                if (adjustedEndHour >= 24 || (adjustedEndHour === 23 && adjustedEndMinutes > 59)) {
                    adjustedEndHour = 23;
                    adjustedEndMinutes = 59;
                }
            }

            // 格式化时间
            const startDate = `${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')}`;

            // 确保结束时间永远不会显示为 24:00
            if (adjustedEndHour >= 24) {
                adjustedEndHour = 23;
                adjustedEndMinutes = 59;
            }

            // 再次特别检查24:00的情况
            if (adjustedEndHour === 24 && adjustedEndMinutes === 0) {
                adjustedEndHour = 23;
                adjustedEndMinutes = 59;
            }

            const endDate = `${adjustedEndHour.toString().padStart(2, '0')}:${adjustedEndMinutes.toString().padStart(2, '0')}`;

            // 更新预览 tip - 修改为使用专用的预览tip状态
            previewTipContent.value = `${startDate} - ${endDate}`;

            // 更新 tip 的 z-index 值，确保它拥有最高层级
            updateTipZIndex();

            // 修改这里：计算预览tip位置，使用限制后的位置
            previewTipPosition.value = {
                x: canvasRect.left + startPosition + (endPosition - startPosition) / 2,
                y: canvasRect.top - 10,
            };

            // 显示预览tip，保持选中tip的状态不变
            previewTipVisible.value = true;

            // 绘制预览时间块
            drawTimeBlocks(true, { startDate, endDate });
        }
        return;
    }

    // 如果不在拖拽状态，隐藏预览 tip
    if (!dragState.value.isDragging) {
        previewTipVisible.value = false;
    }
};

// 修改 handleMouseUp 函数，在移动结束后重新计算 tip 位置
const handleMouseUp = (e: MouseEvent) => {
    // 移除 user-select: none
    document.body.style.userSelect = '';

    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    // 检查鼠标是否在画布外部
    const isOutsideCanvas =
        e.clientX < rect.left ||
        e.clientX > rect.right ||
        e.clientY < rect.top ||
        e.clientY > rect.bottom;

    // 无论如何都应该隐藏预览tip
    previewTipVisible.value = false;

    // 如果点击位置在画布外部，且不是在拖拽或调整大小状态，则取消选中
    if (
        isOutsideCanvas &&
        !dragState.value.isMovingBlock &&
        !dragState.value.isResizing &&
        !dragState.value.isDragging
    ) {
        // 清除选中状态
        selectedTimePlan.value = null;
        tipVisible.value = false;
        dragState.value.canMoveBlock = false;
        dragState.value.clickedPlan = null;
        // 重绘时间块
        drawTimeBlocks();
        return;
    }

    // 首先重置拖拽状态，防止多次触发
    const wasDragging = dragState.value.isDragging;
    const hadMoved = dragState.value.hasMoved;
    const wasMovingBlock = dragState.value.isMovingBlock;
    const wasResizing = dragState.value.isResizing;

    // 立即重置拖拽状态
    dragState.value.isDragging = false;
    dragState.value.hasMoved = false;

    // 获取鼠标在画布坐标系中的位置
    let clickX = e.clientX - rect.left;

    // 处理时间块的移动结束
    if (wasMovingBlock) {
        dragState.value.isMovingBlock = false;
        emit('timeSelect', localTimePlan.value);
        dragState.value.canMoveBlock = true;
        dragState.value.clickedPlan = null;

        // 移动结束后重新计算 tip 位置
        if (selectedTimePlan.value) {
            updateTip();
        }
        return;
    }

    // 处理时间块的调整大小结束
    if (wasResizing) {
        dragState.value.isResizing = false;
        dragState.value.resizeDirection = null;
        emit('timeSelect', localTimePlan.value);
        dragState.value.canMoveBlock = true;
        dragState.value.clickedPlan = null;
        // 如果松开鼠标在画布外部，取消选中状态
        if (isOutsideCanvas) {
            selectedTimePlan.value = null;
            tipVisible.value = false;
        }
        return;
    }

    // 处理拖拽创建新时间块
    if (wasDragging && hadMoved) {
        const gridWidth = canvas.width / 24;
        const quarterWidth = gridWidth / 4;

        // 计算开始时间
        let startPosition = Math.min(dragState.value.startX, clickX);

        // 当从右向左拖出画布时，设置开始位置为0
        if (isOutsideCanvas && clickX < 0) {
            startPosition = 0;
        }

        const startQuarters = Math.round(startPosition / quarterWidth);
        let startHour = Math.floor(startQuarters / 4);
        let startMinutes = (startQuarters % 4) * 15;

        // 处理分钟数为60的情况
        if (startMinutes === 60) {
            startHour += 1;
            startMinutes = 0;
        }

        // 计算结束时间
        let endPosition = Math.max(dragState.value.startX, clickX);

        // 如果从左向右拖出画布时，设置结束位置为画布最大宽度（对应 23:59）
        if (isOutsideCanvas && clickX > canvas.width) {
            endPosition = canvas.width;
        }

        const endQuarters = Math.round(endPosition / quarterWidth);
        let endHour = Math.floor(endQuarters / 4);
        let endMinutes = (endQuarters % 4) * 15;

        // 处理分钟数为60的情况
        if (endMinutes === 60) {
            endHour += 1;
            endMinutes = 0;
        }

        // 处理边界情况，确保不超过 23:59
        if (endHour > 23 || (endHour === 23 && endMinutes > 59)) {
            endHour = 23;
            endMinutes = 59;
        }

        // 格式化时间
        let startDate = `${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')}`;
        let endDate = `${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

        // 处理边界情况
        if (endHour > 23 || (endHour === 23 && endMinutes > 59)) {
            endDate = '23:59';
        }
        if (startHour >= 23 && startMinutes >= 59) {
            startDate = '23:59';
        }

        // 计算时间差（分钟）
        const startTotalMinutes = startHour * 60 + startMinutes;
        const endTotalMinutes = endHour * 60 + endMinutes;
        const timeDiff = endTotalMinutes - startTotalMinutes;

        // 确保最小时间差为15分钟
        if (timeDiff < 15 && startDate !== endDate) {
            // 如果差值不足15分钟，将结束时间调整为开始时间后15分钟
            const adjustedEndTotalMinutes = startTotalMinutes + 15;
            const adjustedEndHour = Math.floor(adjustedEndTotalMinutes / 60);
            const adjustedMinutes = adjustedEndTotalMinutes % 60;

            // 确保调整后的时间不超过23:59
            if (adjustedEndHour >= 24 || (adjustedEndHour === 23 && adjustedMinutes > 59)) {
                endDate = '23:59';
            } else {
                endDate = `${adjustedEndHour.toString().padStart(2, '0')}:${adjustedMinutes.toString().padStart(2, '0')}`;
            }
        }

        // 最后再检查一次结束时间，确保不会有 24:00 的情况
        if (endDate === '24:00' || endHour === 24) {
            endDate = '23:59';
        }

        // 检查时间范围是否有效
        if (startDate !== endDate) {
            // 添加新的时间块
            const newTimeBlock = {
                startDate,
                endDate,
            };

            localTimePlan.value.push(newTimeBlock);

            // 发送更新事件到父组件
            emit('timeSelect', localTimePlan.value);
        }

        // 重置拖拽状态
        dragState.value.startX = 0;
        dragState.value.startY = 0;
        dragState.value.clickedPlan = null;

        // 重绘时间块
        drawTimeBlocks();

        // 如果鼠标在画布外部释放，且不是在拖拽过程中，取消选中
        if (isOutsideCanvas && !dragState.value.hasMoved) {
            selectedTimePlan.value = null;
            tipVisible.value = false;
        }

        return;
    }

    // 如果没有移动，处理选中逻辑
    const clickedPlan = findClickedTimePlan(clickX);

    if (clickedPlan) {
        // 选中时间块
        selectedTimePlan.value = clickedPlan;
        dragState.value.canMoveBlock = true;
    } else {
        // 点击空白处，清除所有状态
        selectedTimePlan.value = null;
        tipVisible.value = false;
        dragState.value.canMoveBlock = false;
        dragState.value.clickedPlan = null;
    }

    drawTimeBlocks();
};

const drawTimeBlocks = (
    isPreview: boolean = false,
    plan?: { startDate: string; endDate: string },
) => {
    const container = rightDayRef.value;
    const gridContainer = container?.querySelector('.grid-container');
    const canvas = canvasRef.value;
    if (!container || !gridContainer || !canvas) return;

    const containerWidth = gridContainer.clientWidth;
    const containerHeight = container.clientHeight;

    // 设置canvas尺寸
    canvas.width = containerWidth;
    canvas.height = containerHeight;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    // 清除之前的绘制内容
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 分离选中的时间块和非选中的时间块
    const selectedPlan = selectedTimePlan.value;
    const nonSelectedPlans = localTimePlan.value.filter(
        (entry) =>
            !selectedPlan ||
            entry.startDate !== selectedPlan.startDate ||
            entry.endDate !== selectedPlan.endDate,
    );

    // 先绘制所有非选中的时间块
    nonSelectedPlans.forEach((entry) => {
        if (!isValidEntry(entry)) return;

        const startTime = parseTime(entry.startDate);
        const endTime = parseTime(entry.endDate);
        if (!startTime || !endTime) return;

        // 计算时间块的位置和宽度
        const gridWidth = canvas.width / 24;
        const x = (startTime.hours + startTime.minutes / 60) * gridWidth;
        const width =
            (endTime.hours + endTime.minutes / 60 - (startTime.hours + startTime.minutes / 60)) *
            gridWidth;

        // 绘制时间块
        ctx.fillStyle = 'rgba(29, 185, 105, 0.3)';
        ctx.beginPath();

        // 绘制圆角矩形
        const radius = 3;
        const padding = 3;

        ctx.moveTo(x + radius, padding);
        ctx.lineTo(x + width - radius, padding);
        ctx.quadraticCurveTo(x + width, padding, x + width, padding + radius);
        ctx.lineTo(x + width, blockHeight.value - padding - radius);
        ctx.quadraticCurveTo(
            x + width,
            blockHeight.value - padding,
            x + width - radius,
            blockHeight.value - padding,
        );
        ctx.lineTo(x + radius, blockHeight.value - padding);
        ctx.quadraticCurveTo(
            x,
            blockHeight.value - padding,
            x,
            blockHeight.value - padding - radius,
        );
        ctx.lineTo(x, padding + radius);
        ctx.quadraticCurveTo(x, padding, x + radius, padding);

        ctx.fill();
    });

    // 最后绘制选中的时间块（如果有）
    if (selectedPlan && isValidEntry(selectedPlan)) {
        const startTime = parseTime(selectedPlan.startDate);
        const endTime = parseTime(selectedPlan.endDate);
        if (startTime && endTime) {
            // 计算时间块的位置和宽度
            const gridWidth = canvas.width / 24;
            const x = (startTime.hours + startTime.minutes / 60) * gridWidth;
            const width =
                (endTime.hours +
                    endTime.minutes / 60 -
                    (startTime.hours + startTime.minutes / 60)) *
                gridWidth;

            // 添加高亮效果
            ctx.save();
            ctx.shadowColor = 'rgba(117, 211, 186, 0.6)';
            ctx.shadowBlur = 3;
            ctx.shadowOffsetX = 0;
            ctx.shadowOffsetY = 0;

            // 使用稍微不同的颜色突出显示选中的时间块
            ctx.fillStyle = 'rgba(29, 185, 105, 0.4)';
            ctx.beginPath();

            // 绘制圆角矩形
            const radius = 3;
            const padding = 3;

            ctx.moveTo(x + radius, padding);
            ctx.lineTo(x + width - radius, padding);
            ctx.quadraticCurveTo(x + width, padding, x + width, padding + radius);
            ctx.lineTo(x + width, blockHeight.value - padding - radius);
            ctx.quadraticCurveTo(
                x + width,
                blockHeight.value - padding,
                x + width - radius,
                blockHeight.value - padding,
            );
            ctx.lineTo(x + radius, blockHeight.value - padding);
            ctx.quadraticCurveTo(
                x,
                blockHeight.value - padding,
                x,
                blockHeight.value - padding - radius,
            );
            ctx.lineTo(x, padding + radius);
            ctx.quadraticCurveTo(x, padding, x + radius, padding);

            ctx.fill();

            // 绘制边框
            ctx.strokeStyle = '#75d3ba';
            ctx.lineWidth = 1.2;
            ctx.stroke();
            ctx.restore();
        }
    }

    // 如果是预览模式，再绘制预览时间块
    if (isPreview && plan) {
        const startTime = parseTime(plan.startDate);
        const endTime = parseTime(plan.endDate);
        if (!startTime || !endTime) return;

        // 计算时间块的位置和宽度
        const gridWidth = canvas.width / 24;
        const x = (startTime.hours + startTime.minutes / 60) * gridWidth;
        const width =
            (endTime.hours + endTime.minutes / 60 - (startTime.hours + startTime.minutes / 60)) *
            gridWidth;

        // 使用不同的颜色或透明度来区分预览时间块
        ctx.fillStyle = 'rgba(29, 185, 105, 0.2)'; // 使用更透明的颜色
        ctx.beginPath();

        // 绘制圆角矩形
        const radius = 3;
        const padding = 3;

        ctx.moveTo(x + radius, padding);
        ctx.lineTo(x + width - radius, padding);
        ctx.quadraticCurveTo(x + width, padding, x + width, padding + radius);
        ctx.lineTo(x + width, blockHeight.value - padding - radius);
        ctx.quadraticCurveTo(
            x + width,
            blockHeight.value - padding,
            x + width - radius,
            blockHeight.value - padding,
        );
        ctx.lineTo(x + radius, blockHeight.value - padding);
        ctx.quadraticCurveTo(
            x,
            blockHeight.value - padding,
            x,
            blockHeight.value - padding - radius,
        );
        ctx.lineTo(x, padding + radius);
        ctx.quadraticCurveTo(x, padding, x + radius, padding);

        ctx.fill();

        // 添加虚线边框以区分预览
        ctx.strokeStyle = 'rgba(29, 185, 105, 0.5)';
        ctx.lineWidth = 1;
        ctx.setLineDash([3, 3]); // 设置虚线样式
        ctx.stroke();
        ctx.setLineDash([]); // 重置虚线样式
    }
};

// 添加 calculateCellWidth 函数
const calculateCellWidth = () => {
    if (!rightDayRef.value) return;
    const gridContainer = rightDayRef.value.querySelector('.grid-container');
    if (!gridContainer) return;

    // 使用网格容器的实际列宽作为单位
    smallestUnit.value = gridContainer.clientWidth / 24;
};

// 修改 updateCanvasSize 函数
const updateCanvasSize = () => {
    if (!rightDayRef.value) return;
    const canvas = canvasRef.value;
    if (!canvas) return;

    const rect = rightDayRef.value.getBoundingClientRect();
    canvas.width = rect.width;
    canvas.height = rect.height;

    // 重新绘制时间块
    drawTimeBlocks();
};

const parseTime = (time?: string): { hours: number; minutes: number } | null => {
    if (!time) return null;
    try {
        const [hours, minutes] = time.split(':').map(Number);
        return isNaN(hours) || isNaN(minutes) ? null : { hours, minutes };
    } catch {
        return null;
    }
};

// 修改 validTimePlans 计算属性

// 修改 handleKeyDown 函数，添加删除选中时间块的功能
const handleKeyDown = (e: KeyboardEvent) => {
    // 如果有选中的时间块，并且按下了 Backspace 或 Delete 键
    if (selectedTimePlan.value && (e.key === 'Backspace' || e.key === 'Delete')) {
        // 确保删除后至少还有一条数据
        if (localTimePlan.value.length > 1) {
            // 找到选中时间块在数组中的索引
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            // 如果找到了，从数组中删除
            if (index !== -1) {
                // 删除时间块
                localTimePlan.value.splice(index, 1);

                // 清除选中状态
                selectedTimePlan.value = null;
                tipVisible.value = false;
                dragState.value.canMoveBlock = false;

                // 重新绘制时间块
                drawTimeBlocks();

                // 发送更新事件到父组件
                emit('timeSelect', localTimePlan.value);
            }
        }

        // 阻止默认行为（如浏览器的后退）
        e.preventDefault();
    }
};

// 修改 watch 监听器，确保在删除时间块时正确处理选中状态
watch(
    () => props.timePlan,
    (newValue) => {
        if (Array.isArray(newValue)) {
            // 保存之前的选中状态
            const previouslySelected = selectedTimePlan.value;

            // 更新本地数据前清除选中状态
            selectedTimePlan.value = null;
            tipVisible.value = false;
            dragState.value.canMoveBlock = false;

            // 更新本地数据
            localTimePlan.value = [...newValue];

            // 只有在非删除操作时才尝试恢复选中状态
            if (previouslySelected && newValue.length >= localTimePlan.value.length) {
                const stillExists = localTimePlan.value.some(
                    (plan) =>
                        plan.startDate === previouslySelected.startDate &&
                        plan.endDate === previouslySelected.endDate,
                );

                if (stillExists) {
                    // 如果时间块仍然存在，恢复选中状态
                    selectedTimePlan.value = previouslySelected;
                    dragState.value.canMoveBlock = true;
                }
            }

            // 重新绘制时间块
            nextTick(() => {
                drawTimeBlocks();
            });
        }
    },
    { deep: true, immediate: true },
);

// 在组件的顶部声明变量
let resizeObserver: ResizeObserver | null = null;

// 添加 mutationObserver 变量
let mutationObserver: MutationObserver | null = null;

// 将 handleLayoutChange 函数移到组件顶层，使其在整个组件中可访问
const handleLayoutChange = () => {
    // 强制重新计算 canvas 大小
    updateCanvasSize();

    // 只有当选中的时间块存在且 tip 应该显示时才更新 tip
    if (selectedTimePlan.value && tipVisible.value) {
        // 延迟一帧，确保 DOM 已完全更新
        requestAnimationFrame(() => {
            updateTip();
        });
    }
};

// 添加滚动事件处理函数
const handleScroll = () => {
    if (selectedTimePlan.value) {
        // 检查选中时间块的可见性
        if (!isSelectedBlockVisible.value) {
            resetTipPosition();
            selectedTimePlan.value = null;
            tipVisible.value = false;
            dragState.value.canMoveBlock = false;
            drawTimeBlocks();
            return;
        }

        // 已经存在观察器的情况下，立即更新 tip 位置
        if (isSelectedBlockVisible.value) {
            // 使用 requestAnimationFrame 确保在下一次重绘前更新位置
            requestAnimationFrame(() => {
                updateTip();
            });
        }

        // 在滚动过程中，尝试重新检查可见性
        if (timeBlockObserver) {
            // 尝试重新检查可见性
            requestAnimationFrame(() => {
                observeSelectedTimeBlock();
            });
        }
    }
};

// 修改 onMounted 钩子
onMounted(() => {
    if (!Array.isArray(props.timePlan)) {
        return;
    }

    updateTipZIndex();

    calculateCellWidth();

    resizeObserver = new ResizeObserver(() => {
        calculateCellWidth();
        drawTimeBlocks();
        // 在容器大小变化时更新 tip 位置
        updateTip();
    });

    if (rightDayRef.value) {
        resizeObserver.observe(rightDayRef.value);
        // 同时监听整个窗口的大小变化
        window.addEventListener('resize', updateTip);

        const canvas = canvasRef.value;
        if (canvas) {
            // 在 canvas 上监听 mousedown
            canvas.addEventListener('mousedown', handleMouseDown);

            // 恢复在 document 上的事件监听
            document.addEventListener('mousemove', handleMouseMove);
            document.addEventListener('mouseup', handleMouseUp);
            document.addEventListener('keydown', handleKeyDown);
        }
    }

    // 添加滚动事件监听
    window.addEventListener('scroll', handleScroll, true);

    // 初始化时间块
    drawTimeBlocks();

    // 创建 MutationObserver 来监听 DOM 变化
    mutationObserver = new MutationObserver(() => {
        // 只有当选中的时间块存在且 tip 应该显示时才更新 tip
        if (selectedTimePlan.value && tipVisible.value) {
            updateTip();
        }
    });

    // 监听整个组件的 DOM 变化
    if (rightDayRef.value) {
        mutationObserver.observe(rightDayRef.value.parentElement || document.body, {
            childList: true,
            subtree: true,
            attributes: true,
        });
    }

    // 监听窗口的 resize 事件
    window.addEventListener('resize', handleLayoutChange);

    // 尝试检测可用的传送门组件
    if (typeof window !== 'undefined') {
        if (!isVue2) {
            // Vue 3 环境
            try {
                // 动态获取 Teleport
                const Vue = require('vue');
                if (Vue.Teleport) {
                    portalComponent.value = 'Teleport';
                    hasPortalSupport.value = true;
                }
            } catch (e) {
                console.error('Failed to import Teleport:', e);
            }
        } else {
            // Vue 2 环境 - 尝试使用 portal-vue
            try {
                // 检查是否安装了 portal-vue
                if (window.Vue && window.Vue.component && window.Vue.component('Portal')) {
                    portalComponent.value = 'portal';
                    hasPortalSupport.value = true;
                }
            } catch (e) {
                console.error('Failed to detect Portal:', e);
            }
        }
    }

    // 初始化 IntersectionObserver 以监控选中时间块的可见性
    if (selectedTimePlan.value) {
        observeSelectedTimeBlock();
    }
});

// 修改 onBeforeUnmount 钩子，确保移除滚动事件监听
onBeforeUnmount(() => {
    if (resizeObserver && rightDayRef.value) {
        resizeObserver.unobserve(rightDayRef.value);
        resizeObserver.disconnect();
    }

    // 确保从 document 上移除事件监听
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
    document.removeEventListener('keydown', handleKeyDown);

    // 移除窗口大小变化监听
    window.removeEventListener('resize', updateTip);
    window.removeEventListener('scroll', handleScroll, true);

    const canvas = canvasRef.value;
    if (canvas) {
        canvas.removeEventListener('mousedown', handleMouseDown);
        // 不需要从 canvas 上移除 mouseup 监听，因为我们在 document 上监听
    }

    // 断开 MutationObserver
    if (mutationObserver) {
        mutationObserver.disconnect();
    }

    // 移除窗口 resize 事件监听
    window.removeEventListener('resize', handleLayoutChange);

    // 清除定时器
    if (updateTipTimeout) {
        clearTimeout(updateTipTimeout);
    }

    // 清除 IntersectionObserver
    if (timeBlockObserver) {
        timeBlockObserver.disconnect();
        timeBlockObserver = null;
    }

    // 清理可能残留的追踪元素
    cleanupTrackingElements();
});

// 添加对 selectedTimePlan 的监听
watch(
    selectedTimePlan,
    (newVal, oldVal) => {
        if (newVal) {
            // 只有当选中了时间块时才更新 tip 和开始观察可见性
            updateTip();
            observeSelectedTimeBlock();
        } else {
            // 确保当选中状态被取消时，提示也被隐藏
            tipVisible.value = false;
            // 清理观察者
            if (timeBlockObserver) {
                timeBlockObserver.disconnect();
                timeBlockObserver = null;
            }
            // 清理追踪元素
            cleanupTrackingElements();
        }
    },
    { deep: true },
);

// 添加 isValidEntry 函数
const isValidEntry = (entry: { startDate: string; endDate?: string }): boolean => {
    return Boolean(
        entry.startDate &&
            entry.endDate &&
            isValidTimeString(entry.startDate) &&
            isValidTimeString(entry.endDate),
    );
};

// 添加 isValidTimeString 函数
const isValidTimeString = (timeStr: string): boolean => {
    if (!timeStr) return false;
    const [hours, minutes] = timeStr.split(':');
    return !isNaN(Number(hours)) && !isNaN(Number(minutes));
};

// 在状态管理部分添加传送门相关变量
// Portal 组件检测
const portalComponent = ref('div');
const hasPortalSupport = ref(false);

// 添加 IntersectionObserver 相关变量
let timeBlockObserver: IntersectionObserver | null = null;
const isSelectedBlockVisible = ref(true);

// 添加一个重置tip位置的函数
const resetTipPosition = () => {
    // 重置tip位置到默认值
    tipPosition.value = { x: 0, y: 0 };
    tipContent.value = '';
};

// 添加观察选中时间块可见性的函数
const observeSelectedTimeBlock = () => {
    // 先清除之前的 observer
    if (timeBlockObserver) {
        timeBlockObserver.disconnect();
        timeBlockObserver = null;
    }

    // 清理可能存在的追踪元素
    cleanupTrackingElements();

    // 如果没有选中的时间块，不需要监控
    if (!selectedTimePlan.value) {
        isSelectedBlockVisible.value = true;
        return;
    }

    const canvas = canvasRef.value;
    if (!canvas) return;

    // 获取可能的弹窗容器（父元素）
    // 向上查找可能的滚动容器作为 root
    let scrollContainer = canvas.parentElement;
    while (scrollContainer) {
        // 检查是否是滚动容器
        const style = window.getComputedStyle(scrollContainer);
        if (
            ['auto', 'scroll'].includes(style.overflowY) ||
            ['auto', 'scroll'].includes(style.overflow) ||
            scrollContainer.classList.contains('modal') ||
            scrollContainer.classList.contains('dialog') ||
            scrollContainer.classList.contains('popup')
        ) {
            break; // 找到了可能的滚动容器
        }
        scrollContainer = scrollContainer.parentElement;
    }

    // 获取选中时间块的位置信息
    const timePosition = getTimeBlockPosition(
        selectedTimePlan.value.startDate,
        selectedTimePlan.value.endDate,
    );

    if (timePosition.startX !== 0 || timePosition.endX !== 0) {
        // 创建一个临时 div 元素来跟踪选中时间块的位置
        const trackerDiv = document.createElement('div');
        trackerDiv.style.position = 'absolute';
        trackerDiv.style.pointerEvents = 'none';
        trackerDiv.style.opacity = '0';
        // 添加数据属性用于后续清理
        trackerDiv.setAttribute('data-time-block-tracker', 'true');

        // 设置 div 的位置和大小，对应于选中的时间块
        trackerDiv.style.top = `0px`;
        trackerDiv.style.left = `${timePosition.startX}px`;
        trackerDiv.style.width = `${timePosition.endX - timePosition.startX}px`;
        trackerDiv.style.height = `${blockHeight.value}px`;

        // 添加到 canvas 的父元素
        canvas.parentElement?.appendChild(trackerDiv);

        // 创建新的 IntersectionObserver
        timeBlockObserver = new IntersectionObserver(
            (entries) => {
                // 检查时间块是否在视口内
                const isVisible = entries.some((entry) => entry.isIntersecting);

                // 更新可见性状态
                isSelectedBlockVisible.value = isVisible;

                // 如果时间块完全不可见，取消选中状态
                if (!isVisible && selectedTimePlan.value) {
                    // 重要：在取消选中之前重置tip位置
                    resetTipPosition();

                    selectedTimePlan.value = null;
                    tipVisible.value = false;
                    dragState.value.canMoveBlock = false;
                    drawTimeBlocks();

                    // 移除临时元素
                    if (trackerDiv.parentElement) {
                        trackerDiv.parentElement.removeChild(trackerDiv);
                    }
                }
            },
            {
                threshold: 0, // 完全不可见时触发
                root: scrollContainer || null, // 使用找到的滚动容器或视口作为根元素
                rootMargin: '0px', // 不使用边距，直接以元素边界为准
            },
        );

        // 监控临时 div 元素的可见性
        timeBlockObserver.observe(trackerDiv);
        return;
    }

    // 如果没有有效的选中时间块位置信息，退回到观察整个 canvas
    timeBlockObserver = new IntersectionObserver(
        (entries) => {
            // 检查时间块是否在视口内
            const isVisible = entries.some((entry) => entry.isIntersecting);

            // 更新可见性状态
            isSelectedBlockVisible.value = isVisible;

            // 如果时间块完全不可见，取消选中状态
            if (!isVisible && selectedTimePlan.value) {
                // 重要：在取消选中之前重置tip位置
                resetTipPosition();

                selectedTimePlan.value = null;
                tipVisible.value = false;
                dragState.value.canMoveBlock = false;
                drawTimeBlocks();
            }
        },
        {
            threshold: 0, // 完全不可见时触发
            root: scrollContainer || null, // 使用找到的滚动容器或视口作为根元素
            rootMargin: '0px', // 不使用边距，直接以元素边界为准
        },
    );

    // 监控整个 canvas 元素的可见性
    timeBlockObserver.observe(canvas);
};

// 添加清理临时追踪元素的函数
const cleanupTrackingElements = () => {
    // 查找和清理所有可能的追踪元素
    const canvas = canvasRef.value;
    if (canvas && canvas.parentElement) {
        const parent = canvas.parentElement;
        const trackers = parent.querySelectorAll('[data-time-block-tracker]');
        trackers.forEach((tracker) => {
            if (tracker.parentElement) {
                tracker.parentElement.removeChild(tracker);
            }
        });
    }
};
</script>

<style scoped lang="less">
.time-echarts {
    position: relative;
    @border-color: #cccccc;
    @primary-color: rgba(0, 117, 255, 0.3);
    @hover-color: rgba(0, 117, 255, 0.5);

    display: flex;
    justify-content: center;
    align-items: center;

    .contents-day {
        width: 100%;
        margin-bottom: 20px;
    }

    .bottom {
        display: flex;
    }

    .right-day {
        width: 100%;
        position: relative;
        background-color: #fff;
        border-bottom: 1px solid @border-color;
        border-top: 1px solid @border-color;
        box-sizing: border-box;

        .grid-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: grid;
            grid-template-columns: repeat(24, 1fr);
            pointer-events: none; // 让鼠标事件穿透到canvas

            .grid-column {
                border-right: 1px solid #cccccc;

                &:first-child {
                    border-left: 1px solid #cccccc;
                }
            }
        }

        canvas {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            width: 100%;
            pointer-events: auto; // 允许鼠标事件
        }
    }

    .bottomhour-day {
        display: flex;
        flex: 1;
        height: 24px;
    }

    .ul-day {
        margin-left: 40px;
        flex-shrink: 0;
        display: flex;
        flex-grow: 1;
        padding: 0;

        li {
            flex: 1;
            height: 24px;
            text-align: center;
            line-height: 24px;
            font-size: 11px;
            padding: 0;
            box-sizing: border-box;
            color: #7e8494;
            list-style: none;
            &:last-child {
                margin-right: 1px;
            }
        }
    }

    .weekday-day {
        font-size: 11px;
        width: 40px;
        color: #7e8494;
        text-align: center;
    }

    .tip-container {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        pointer-events: none;
    }

    .time-tip {
        position: absolute;
        background: linear-gradient(to bottom, #2c3e50, #34495e);
        color: white;
        padding: 5px 10px;
        border-radius: 4px;
        font-size: 12px;
        pointer-events: none;
        white-space: nowrap;

        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
        letter-spacing: 0.5px;
        font-weight: 500;
        line-height: normal;
        border: 1px solid rgba(255, 255, 255, 0.1);

        &::after {
            content: '';
            position: absolute;
            left: 50%;
            bottom: -5px;
            transform: translateX(-50%);
            border-left: 5px solid transparent;
            border-right: 5px solid transparent;
            border-top: 5px solid #34495e;
            filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.1));
        }

        animation: tipFadeIn 0.2s ease-out;

        &.preview-tip {
            background: linear-gradient(to bottom, #3a5273, #4a6284);
        }
    }

    @keyframes tipFadeIn {
        from {
            opacity: 0;
            transform: translate(-50%, -90%);
        }
        to {
            opacity: 1;
            transform: translate(-50%, -100%);
        }
    }
}
</style>
